package appllo5;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import java.beans.PropertyVetoException;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URLEncoder;
import java.sql.*;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Server1 {

    static Logger logger = Logger.getLogger("Server.class");

    static Map<String, Socket> socketmps = new ConcurrentHashMap<String, Socket>();
    static final String DB_URL = "jdbc:mysql://47.92.165.115:3306/thinkcmf5?characterEncoding=utf-8&useSSL=false";
    static final String USER = "root";
    static final String PASS = "Erxi123!";
    static final ComboPooledDataSource ds = new ComboPooledDataSource();
    ;

//    static Connection conn = null;
//    static ResultSet rs = null;
//    static PreparedStatement ps = null;

    static {
//        try {
//            Class.forName("com.mysql.jdbc.Driver");
//        } catch (ClassNotFoundException e) {
//            e.printStackTrace();
//        }
        ds.setJdbcUrl(DB_URL);
        ds.setUser(USER);
        ds.setPassword(PASS);
        try {
            ds.setDriverClass("com.mysql.jdbc.Driver");
        } catch (PropertyVetoException e) {
            e.printStackTrace();
        }
        ds.setAcquireIncrement(5);
        ds.setInitialPoolSize(10);
        ds.setMinPoolSize(2);
        ds.setMaxPoolSize(100);
    }

    public static void handleDeviceData(Socket socket) throws IOException, SQLException, InterruptedException {

        InputStreamReader input = new InputStreamReader(socket.getInputStream(), "UTF-8");
        Writer writer = new OutputStreamWriter(socket.getOutputStream(), "UTF-8");
        InputStream is = socket.getInputStream();
        char[] chars = new char[1024];
        int len;
        while (true) {
            if (is.available() == 0) {
                continue;
            }
            if ((len = input.read(chars)) != -1) {
                String deviceData = new String(chars, 0, len);

//                Date d = new Date(System.currentTimeMillis());
//                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

                logger.log(Level.INFO, deviceData);
                if (deviceData.endsWith("]") && deviceData.startsWith("[")) {
                    String[] dataArr1 = deviceData.split("\\]\\[");
                    for (int ii = 0; ii < dataArr1.length; ii++) {
                        deviceData = dataArr1[ii];
                        if (dataArr1.length > 1) {
                            if (ii == 0) {
                                deviceData += "]";
                            } else {
                                deviceData = "[" + deviceData;
                            }
                        }
                        if (deviceData.contains("LK") || deviceData.contains("KA") || deviceData.contains("AL")) {
                            int slen = deviceData.indexOf(",");
                            String returnstr = null;
                            if (slen > 0) {
                                returnstr = "[" + deviceData.substring(1, slen) + "]";
                            } else {
                                returnstr = "[" + deviceData.substring(1);
                            }
                            String[] returnArr = returnstr.split("\\*");
                            returnArr[2] = "0002";
                            StringBuffer sbReturn = new StringBuffer();
                            for (int i = 0; i < returnArr.length; i++) {
                                sbReturn.append(returnArr[i]);
                                if (i < returnArr.length - 1) {
                                    sbReturn.append("*");
                                }
                            }

                            writer.write(sbReturn.toString());
                            writer.flush();
                        }

                        String[] dataArr = deviceData.split("\\*");
                        String deviceImei = dataArr[1];

                        Server1.socketmps.put(deviceImei, socket);
                        if (deviceData.contains("*UD,") || deviceData.contains("*UD2,")) {
                            // String[] command_device_head = deviceData.split("\\*");
                            // String command_upload = command_device_head[0] + "*" + command_device_head[1]
                            // + "*0009*UPLOAD,3600]";
                            // System.out.println(command_upload);
                            // writer.write(command_upload);
                            // writer.flush();
                            int start = deviceData.indexOf(",");
                            deviceData = deviceData.substring(start + 1, deviceData.length() - 1);
                            dataArr = deviceData.split(",");
                            String date = dataArr[0];
                            String time = dataArr[1];
                            Boolean isGpgEffect = "A".equals(dataArr[2]) ? true : false; // gps是否有效
                            int is_gps = isGpgEffect ? 1 : 0;
                            String latitude = dataArr[3];
                            String longitude = dataArr[5];
                            String speed = dataArr[7];
                            String direction = dataArr[8]; // 方向
                            String altitude = dataArr[9]; // 海拔
                            String satellite = dataArr[10]; // 卫星个数
                            String signalIntensity = dataArr[11]; // 信号强度
                            String electricQuantity = dataArr[12]; // 电量
                            // String stepNumber = dataArr[13]; // 计步数
                            // String flippingTimes = dataArr[14]; // 翻转次数
                            // String deviceStatus = dataArr[15]; // 终端状态
                            if (!isGpgEffect) {
                                int base_count = Integer.parseInt(dataArr[16]) - 1;
                                String mcc = dataArr[18];
                                String mnc = dataArr[19];
                                String bts = mcc + "," + mnc + "," + dataArr[20] + "," + dataArr[21] + "," + dataArr[22];
                                String nearbts = "";
                                String json = "";
                                for (int i = 23; i < 23 + base_count * 3; i += 3) {
                                    if (i > 23) {
                                        nearbts += ",";
                                    }
                                    nearbts += mcc + "," + mnc + "," + dataArr[i] + "," + dataArr[i + 1] + "," + dataArr[i + 2];
                                }

//                            System.out.println(bts);
//                            System.out.println(nearbts);
                                int wifiStart = 23 + base_count * 3;
                                int lengthData = dataArr.length;
                                if (lengthData > wifiStart) {
                                    int wifiCount = Integer.parseInt(dataArr[wifiStart]);
//                                System.out.println(dataArr[wifiStart]);
                                    String[] wifiInfo = new String[wifiCount];
                                    for (int i = wifiStart + 1, j = 0; i < lengthData; i += 3, j++) {
                                        String[] wifiStr = new String[3];
                                        wifiStr[0] = dataArr[i + 1];
                                        wifiStr[1] = dataArr[i + 2];
                                        wifiStr[2] = dataArr[i];
                                        wifiInfo[j] = StringUtils.join(wifiStr, ",");
                                    }
                                    String macs = StringUtils.join(wifiInfo, "|");
                                    String url = "http://apilocate.amap.com/position?accesstype=1&imei=" + deviceImei
                                            + "&key=ae7676899c4d762b8eade0ebe9d43c15&macs=" + URLEncoder.encode(macs, "UTF-8");
                                    json = sendWeixinMessage(url);
                                } else {
                                    String url = "http://apilocate.amap.com/position?accesstype=0&cdma=0&imei=" + deviceImei
                                            + "&key=ae7676899c4d762b8eade0ebe9d43c15&bts=" + bts + "&nearbts=" + nearbts;
                                    json = sendWeixinMessage(url);
                                }
                                ObjectMapper mapper = new ObjectMapper();
                                Map<?, ?> map = mapper.readValue(json, Map.class);
                                LinkedHashMap<?, ?> result = (LinkedHashMap<?, ?>) map.get("result");
                                String[] location = result.get("location").toString().split(",");
                                longitude = location[0];
                                latitude = location[1];
                            }
//                            conn = DriverManager.getConnection(DB_URL, USER, PASS);
                            Connection conn = ds.getConnection();
                            Statement stmt = conn.createStatement();
                            String sql_check_homeon = "select * from erxi_device where imei='" + deviceImei + "'";
                            ResultSet rs = stmt.executeQuery(sql_check_homeon);
                            int is_home = 0;
                            int at_home = 0;
                            double center_latitude = 0d;
                            double center_longitude = 0d;
                            int range = 0;
                            int device_id = 0;
                            while (rs.next()) {
                                device_id = rs.getInt("id");
                                is_home = rs.getInt("is_home");
                                at_home = rs.getInt("at_home");
                                range = rs.getInt("range");
                                center_latitude = rs.getDouble("center_latitude");
                                center_longitude = rs.getDouble("center_longitude");
                                // 打开链接
                            }
                            stmt.close();
                            // 判断是否开启了电子围栏
                            if (is_home == 1) {
                                // double absLatitude = Math.abs(center_latitude -
                                // Double.parseDouble(latitude));
                                // double absLongitude = Math.abs(center_longitude -
                                // Double.parseDouble(longitude));
                                // double distance = Math.sqrt(absLatitude * absLatitude + absLongitude *
                                // absLongitude);
                                String weixinReturn = "";
                                if (center_latitude != 0 && center_longitude != 0) {
                                    double distance = getDistance(Double.parseDouble(latitude),
                                            Double.parseDouble(longitude), center_latitude, center_longitude);
                                    // System.out.println("distance:"+distance);
                                    // System.out.println("latitude:"+latitude+"longitude:"+longitude+"center_latitude:"+center_latitude+"center_longitude:"+center_longitude);
                                    if (range > 0) {
                                        if (distance >= range) {
                                            // 宠物离开围栏，开始报警
                                            at_home = 0;
                                            String url =
                                                    "https://www.ju2xi.com/user/weixin/getindusinfo?imei=" + deviceImei
                                                            + "&athome=" + at_home + "&latitude=" + latitude
                                                            + "&longtitude=" + longitude;
                                            weixinReturn = sendWeixinMessage(url);
//                                        System.out.println("distance:" + distance + ";range:" + range + ";url:" + url + ";weixinReturn:" + weixinReturn);
                                        }
//                                    if (distance < range) {
                                        // 宠物进入围栏，开始报警
//                                        at_home = 1;
                                        // weixinReturn = sendWeixinMessage(
                                        // "www.ju2xi.com/user/weixin/getindusinfo?imei=" + deviceImei + "&athome="
                                        // + at_home + "&latitude=" + latitude + "&longtitude=" + longitude);
//                                    }
                                        // System.out.println("https://www.ju2xi.com/user/weixin/getindusinfo?imei="
                                        // + deviceImei + "&athome=" + at_home + "&latitude=" + latitude
                                        // + "&longtitude=" + longitude);
                                    }
                                }
                            }

                            //计算WiFi定位的误差
//                        double pre_times = System.currentTimeMillis() - 3600 * 3000;
//                            String sql_select_last_data = "select id,latitude,longitude from erxi_device_data where imei=? order by id desc limit 1";
//                            PreparedStatement ps_select_last_data = conn.prepareStatement(sql_select_last_data);
//                            ps_select_last_data.setString(1, deviceImei);
////                        ps_select_last_data.setDouble(2, pre_times);
//                            ResultSet rs_last_data = ps_select_last_data.executeQuery();
//                            boolean is_save_data = false;
//                            int device_data_id = 0;
//                            if (rs_last_data.next()) {
//                                String last_latitude = rs_last_data.getString("latitude");
//                                String last_longitude = rs_last_data.getString("longitude");
//                                device_data_id = rs_last_data.getInt("id");
//                                Double l_lat = Double.parseDouble(last_latitude);
//                                Double l_log = Double.parseDouble(last_longitude);
//                                Double n_lat = Double.parseDouble(latitude);
//                                Double n_log = Double.parseDouble(longitude);
//                                if (Math.abs(l_lat - n_lat) > 0.0013 || Math.abs(l_log - n_log) > 0.0013) {
//                                    is_save_data = true;
//                                }
//                            } else {
//                                is_save_data = true;
//                            }
//                            ps_select_last_data.close();
//                            if (is_save_data) {
                                String sql_insert_data = "insert into erxi_device_data (`imei`,`date`,`time`,latitude,longitude,speed,direction,satellite,altitude,signalIntensity,electricQuantity,create_time,is_gps) values (?,?,?,?,?,?,?,?,?,?,?,?,?)";
                                PreparedStatement ps = conn.prepareStatement(sql_insert_data);
                                ps.setString(1, deviceImei);
                                ps.setString(2, date);
                                ps.setString(3, time);
                                ps.setString(4, latitude);
                                ps.setString(5, longitude);
                                ps.setString(6, speed);
                                ps.setString(7, direction);
                                ps.setString(8, satellite);
                                ps.setString(9, altitude);
                                ps.setString(10, signalIntensity);
                                ps.setString(11, electricQuantity);
                                ps.setDouble(12, System.currentTimeMillis());
                                ps.setInt(13, is_gps);
                                ps.execute(); // 执行sql语句
                                ps.close();
//                            } else {
//                                if (device_data_id != 0) {
//                                    String sqlUpdateDevice = "update erxi_device_data set create_time=?,is_gps=? where id=?";
//                                    PreparedStatement ps_update_device = conn.prepareStatement(sqlUpdateDevice);
////                                ps_update_device.setString(1, latitude);
////                                ps_update_device.setString(2, longitude);
//                                    ps_update_device.setDouble(1, System.currentTimeMillis());
//                                    ps_update_device.setInt(2, is_gps);
//                                    ps_update_device.setInt(3, device_data_id);
//                                    ps_update_device.execute(); // 执行sql语句
//                                    ps_update_device.close();
//                                }
//                            }
                            conn.close();
                            //设置at_home，暂不用
//                        String sqlUpdateDevice = "update erxi_device set at_home=? where id=?";
//                        PreparedStatement ps_update_device = conn.prepareStatement(sqlUpdateDevice);
//                        ps_update_device.setLong(1, at_home);
//                        ps_update_device.setLong(2, device_id);
//                        ps_update_device.execute(); // 执行sql语句
                        }
                    }
                    Thread.sleep(5000);
                } else {
                    input.close();
                    writer.close();
                    socket.close();
                    return;
                }
            }
        }
    }

    public static String sendWeixinMessage(String url) throws ClientProtocolException, IOException {
        CloseableHttpClient httpclient = HttpClients.createDefault();
        HttpGet httpget = new HttpGet(url);
        CloseableHttpResponse response = httpclient.execute(httpget);
        String str = "";
        try {
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                // long len = entity.getContentLength();
                // if (len != -1 && len < 2048) {
                // System.out.println(EntityUtils.toString(entity));
                // } else {
                // // Stream content out
                // System.out.println(EntityUtils.toString(entity));
                // }
                str = EntityUtils.toString(entity);
            }
        } finally {
            response.close();
        }
        return str;
    }

    public static void main(String[] args) throws IOException {
        new Thread(new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    // System.out.println(Server.socketmps);
                    Set<Map.Entry<String, Socket>> set = Server1.socketmps.entrySet();
                    Iterator<Map.Entry<String, Socket>> it = set.iterator();
                    while (it.hasNext()) {
                        Map.Entry<String, Socket> item = it.next();
                        String key = item.getKey();
                        Socket socket = item.getValue();
                        boolean isconnection = checkSocketAlivle(socket);
                        if (!isconnection) {
                            try {
                                socket.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                                logger.log(Level.INFO, e.getMessage());
                            } finally {
                                Server1.socketmps.remove(key);
                            }
                        }
                        // try {
                        // socket.sendUrgentData(0);
                        // } catch (IOException e) {
                        // // e.printStackTrace();
                        // try {
                        // socket.close();
                        // } catch (IOException e1) {
                        // e1.printStackTrace();
                        // }
                        // Server.socketmps.remove(key);
                        // }

                    }
                    try {
                        Thread.sleep(300 * 1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        logger.log(Level.INFO, e.getMessage());
                    }
                }
            }
        })).start();

        new Thread(new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        LoggerUtil.setLogingProperties(logger);
                    } catch (IOException e) {
                        e.printStackTrace();
                        logger.log(Level.INFO, e.getMessage());
                    }
                    try {
                        Thread.sleep(3600000 * 12);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        logger.log(Level.INFO, e.getMessage());
                    }
                }
            }
        })).start();

        // 监听设备发送的数据
        new Thread(new Runnable() {
            @SuppressWarnings("resource")
            @Override
            public void run() {
                int port = 8888;
                ServerSocket server = null;
                try {
                    server = new ServerSocket(port);
                } catch (IOException e) {
                    e.printStackTrace();
                    logger.log(Level.INFO, e.getMessage());
                }
                System.out.println("正在监听8888端口");
                while (true) {
                    Socket socket = null;
                    try {
                        socket = server.accept();
                        socket.setKeepAlive(true);
                        socket.setSoTimeout(300 * 1000);
                        new Thread(new Task(socket)).start();
                    } catch (IOException e) {
                        e.printStackTrace();
                        logger.log(Level.INFO, e.getMessage());
                    }
                }
            }
        }).start();

        // 监听客户发送的命令
        new Thread(new Runnable() {
            @SuppressWarnings("resource")
            @Override
            public void run() {
                int port = 8889;
                System.out.println("正在监听8889端口");
                ServerSocket server = null;
                try {
                    server = new ServerSocket(port);
                } catch (IOException e) {
                    e.printStackTrace();
                    logger.log(Level.INFO, e.getMessage());
                }
                while (true) {
                    // server尝试接收其他Socket的连接请求，server的accept方法是阻塞式的
                    Socket socket = null;
                    try {
                        socket = server.accept();
                    } catch (IOException e) {
                        e.printStackTrace();
                        logger.log(Level.INFO, e.getMessage());
                    }
                    // System.out.println("与客户端建立新连接...");
                    try {
                        new Thread(new ClientCommand(socket)).start();
                    } catch (IOException e) {
                        e.printStackTrace();
                        logger.log(Level.INFO, e.getMessage());
                    }
                }
            }
        }).start();
    }

    /**
     * 处理Socket请求的线程类
     */
    static class Task implements Runnable {

        private Socket socket;

        public Task(Socket socket) throws IOException {
            // System.out.println("socket:" + socket.getPort());
            this.socket = socket;
        }

        @Override
        public void run() {
            try {
                Server1.handleDeviceData(socket);
            } catch (Exception e) {
                e.printStackTrace();
                logger.log(Level.INFO, e.getMessage());
                try {
                    this.socket.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                    logger.log(Level.INFO, e.getMessage());
                }
                // System.out.println("socket closed");
                e.printStackTrace();
            }
        }

    }

    /**
     * 处理Socket请求的线程类
     */
    static class ClientCommand implements Runnable {

        private Socket socket;

        public ClientCommand(Socket socket) throws IOException {
            // System.out.println("socket:" + socket.getPort());
            this.socket = socket;
        }

        @Override
        public void run() {
            try {
                BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
                Writer writer = new OutputStreamWriter(socket.getOutputStream(), "UTF-8");
                char[] chars = new char[10240];
                int int_cmd = br.read(chars,0,10240);
                String str_cmd = new String(chars, 0, int_cmd);
                String[] commandarr = str_cmd.split("&&");
                String imei = commandarr[0];
                Socket socketDevice = Server1.socketmps.get(imei);
//                System.out.println(socketmps);
//                System.out.println(socketDevice);
//                if (socketDevice != null) {
//                    BufferedReader brDevice = new BufferedReader(
//                            new InputStreamReader(socketDevice.getInputStream(), "UTF-8"));
//                    InputStreamReader input = new InputStreamReader(socket.getInputStream(), "UTF-8");
//                    Writer writerDevice = new OutputStreamWriter(socketDevice.getOutputStream(), "UTF-8");
                    String[] cmd = commandarr[1].split("\\*");
                    String coomandServer = "";
                    String cmdLength = String.valueOf(cmd[3].length() - 1);
                    int intLeng = cmdLength.length();
                    if (cmd.length == 4) {
                        int de = 4 - intLeng;
                        for (int i = 0; i < de; i++) {
                            coomandServer += "0";
                        }
                        cmd[2] = coomandServer + cmdLength;
                    }
//                    writerDevice.write(StringUtils.join(cmd, '*'));
                    logger.log(Level.INFO, "client command:"+StringUtils.join(cmd, '*'));
//                    writerDevice.flush();
//                    char[] chars = new char[1024];
//                    int deviceReturn = input.read(chars);
//                    if(deviceReturn!=-1){
//
//                    }
                    writer.write("ok\n");
//                } else {
//                    writer.write("false\n");
//                }
                writer.flush();
                br.close();
                writer.close();
                socket.close();

            } catch (Exception e) {
                e.printStackTrace();
                logger.log(Level.INFO, e.getMessage());
            }
        }

        // private void handlerSocket() throws Exception {
        //
        // }

    }

    public static boolean checkSocketAlivle(Socket socket) {
        try {
            socket.sendUrgentData(0);
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            logger.log(Level.INFO, e.getMessage());
            return false;
        }

    }

    private static double EARTH_RADIUS = 6378.137;

    private static double rad(double d) {
        return d * Math.PI / 180.0;
    }

    /**
     * 通过经纬度获取距离(单位：米)
     *
     * @param lat1
     * @param lng1
     * @param lat2
     * @param lng2
     * @return
     */
    public static double getDistance(double lat1, double lng1, double lat2, double lng2) {
        double radLat1 = rad(lat1);
        double radLat2 = rad(lat2);
        double a = radLat1 - radLat2;
        double b = rad(lng1) - rad(lng2);
        double s = 2 * Math.asin(Math.sqrt(
                Math.pow(Math.sin(a / 2), 2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
        s = s * EARTH_RADIUS;
        s = Math.round(s * 10000d) / 10000d;
        s = s * 1000;
        return s;
    }

}
